﻿using mvcForum.Core.Interfaces.Data;
using System;
using System.Linq.Expressions;


namespace mvcForum.Core.Specifications {

	public static class PostSpecifications {

		/// <summary>
		/// This specification finds all posts on a given topic that are not deleted or quarantined.
		/// </summary>
		public class NotSpamNotDeleted : ISpecification<Post> {
			private readonly Topic topic;

			public NotSpamNotDeleted(Topic topic) {
				this.topic = topic;
			}

			public Expression<Func<Post, Boolean>> IsSatisfied {
				get {
					return x => x.TopicId == topic.Id &&
								((x.FlagValue & (Int32)PostFlag.Deleted) == 0) &&
								((x.FlagValue & (Int32)PostFlag.Quarantined) == 0);
				}
			}
		}

		/// <summary>
		/// This Specification finds all posts on a given topic that are not quarantined, if a user is given,
		/// deleted posts by that user will also be found.
		/// </summary>
		public class NotSpam : ISpecification<Post> {
			private readonly Topic topic;
			private readonly ForumUser user;
			private readonly Boolean moderator;

			public NotSpam(Topic topic, ForumUser user, Boolean moderator) {
				this.topic = topic;
				this.user = user;
				this.moderator = moderator;
			}

			public Expression<Func<Post, Boolean>> IsSatisfied {
				get {
					// TODO: Merge this into one expression?!?!
					if (this.user != null) {
						// TODO: The moderators should get the quarantined posts too????
						return x => x.TopicId == topic.Id &&
									(
							// Not quarantined and not deleted!
										((x.FlagValue & (Int32)PostFlag.Quarantined) == 0 &&
											(x.FlagValue & (Int32)PostFlag.Deleted) == 0) ||
							// Or a moderator (will see all)
										moderator ||
							// Or the author (will see his own deleted posts).
										(x.AuthorId == this.user.Id && (x.FlagValue & (Int32)PostFlag.Deleted) != 0)
									);
					}
					else {
						// TODO: The moderators should get the quarantined posts too????
						return x => x.TopicId == topic.Id &&
									(
							// Not quarantined and not deleted!
										((x.FlagValue & (Int32)PostFlag.Quarantined) == 0 &&
											(x.FlagValue & (Int32)PostFlag.Deleted) == 0) ||
							// Or a moderator (will see all)
										moderator
									);
					}
				}
			}
		}

		/// <summary>
		/// This specification finds all posts on a given topics.
		/// </summary>
		public class All : ISpecification<Post> {
			private readonly Topic topic;

			public All(Topic topic) {
				this.topic = topic;
			}

			public Expression<Func<Post, Boolean>> IsSatisfied {
				get {
					return x => x.TopicId == topic.Id;
				}
			}
		}

		/// <summary>
		/// The specification finds all posts by the given author posted recently (within the given interval).
		/// </summary>
		public class RecentlyByAuthor : ISpecification<Post> {
			private readonly ForumUser author;
			private readonly Int32 interval;

			/// <summary>
			/// 
			/// </summary>
			/// <param name="author">The author of the posts.</param>
			/// <param name="interval">The interval in minutes.</param>
			public RecentlyByAuthor(ForumUser author, Int32 interval) {
				this.author = author;
				this.interval = interval;
			}

			public Expression<Func<Post, bool>> IsSatisfied {
				get {
					DateTime dt = DateTime.UtcNow.AddMinutes(-1 * interval);
					return x => x.AuthorId == author.Id && x.Posted >= dt;
				}
			}
		}

		/// <summary>
		/// This specification finds all (not deleted or quarantined) posts by a given author.
		/// </summary>
		public class AllVisibleByAuthor : ISpecification<Post> {
			private readonly ForumUser author;

			public AllVisibleByAuthor(ForumUser author) {
				this.author = author;
			}

			public Expression<Func<Post, Boolean>> IsSatisfied {
				get {
					return x => x.AuthorId == author.Id && (x.FlagValue & (Int32)PostFlag.Deleted) == 0 && (x.FlagValue & (Int32)PostFlag.Quarantined) == 0;
				}
			}
		}

		public class Visible : ISpecification<Post> {

			public Visible() { }

			public Expression<Func<Post, Boolean>> IsSatisfied {
				get {
					return p => p.FlagValue == (Int32)PostFlag.None;
				}
			}
		}

		public class RegularListing : ISpecification<Post> {
			private readonly Topic topic;
			private readonly Boolean getDeleted;

			public RegularListing(Topic topic, Boolean getDeleted) {
				this.topic = topic;
				this.getDeleted = getDeleted;
			}

			public Expression<Func<Post, Boolean>> IsSatisfied {
				get {
					// TODO: Spam score ????
					if (this.getDeleted) {
						return p => p.Topic.Id == this.topic.Id &&
									(p.FlagValue == (Int32)PostFlag.Deleted || p.FlagValue == (Int32)PostFlag.None);
					}
					else {
						return p => p.Topic.Id == this.topic.Id &&
									p.FlagValue == (Int32)PostFlag.None;
					}
				}
			}
		}
	}
}